module common_lib_vld_rdy #(
    parameter MODE = 0,
    parameter DATA_WIDTH = 8,
    parameter FIFO_DEPTH = 4,
    parameter RST_EN = 1
) (
    input logic clk,
    input logic rst_n,
    input logic vld_i,
    input logic rdy_i,
    input logic [DATA_WIDTH-1:0] data_i,
    output logic vld_o,
    output logic rdy_o,
    output logic [DATA_WIDTH-1:0] data_o
);

    logic [$clog2(FIFO_DEPTH):0] fifo_wr_ptr;
    logic [$clog2(FIFO_DEPTH):0] fifo_rd_ptr;
    logic                        fifo_empty;
    logic                        fifo_full;
    logic [      DATA_WIDTH-1:0] fifo_mem    [FIFO_DEPTH-1:0];

    genvar i;
    if (MODE == 0) begin
        assign rdy_i = (!vld_o || rdy_o);
        always_ff @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                vld_o <= 0;
            end else begin
                if (rdy_i) begin
                    vld_o <= vld_i;
                end
            end
        end
        if (RST_EN == 1) begin
            always_ff @(posedge clk or negedge rst_n) begin
                if (!rst_n) begin
                    data_o <= 0;
                end else if (vld_i && rdy_i) begin
                    data_o <= data_i;
                end
            end
        end else begin
            always_ff @(posedge clk) begin
                if (vld_i && rdy_i) begin
                    data_o <= data_i;
                end
            end
        end
    end else if (MODE == 1) begin
        assign rdy_i  = rdy_o;
        assign vld_o  = vld_i;
        assign data_o = data_i;
    end else if (MODE == 2) begin
        assign rdy_i = !vld_o;
        always_ff @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                vld_o <= 0;
            end else begin
                if (vld_i && rdy_i) begin
                    vld_o <= 1;
                end else if (rdy_o) begin
                    vld_o <= 0;
                end
            end
        end
        if (RST_EN == 1) begin
            always_ff @(posedge clk or negedge rst_n) begin
                if (!rst_n) begin
                    data_o <= 0;
                end else if (vld_i && rdy_i) begin
                    data_o <= data_i;
                end
            end
        end else begin
            always_ff @(posedge clk) begin
                if (vld_i && rdy_i) begin
                    data_o <= data_i;
                end
            end
        end
    end else if (MODE == 3) begin
        assign fifo_empty = (fifo_wr_ptr == fifo_rd_ptr);
        assign fifo_full = (fifo_rd_ptr[$clog2(
            FIFO_DEPTH
        )] != fifo_wr_ptr[$clog2(
            FIFO_DEPTH
        ):0]) && (fifo_wr_ptr[$clog2(
            FIFO_DEPTH
        )-1:0] == fifo_rd_ptr[$clog2(
            FIFO_DEPTH
        )-1:0]);
        assign rdy_i = !fifo_full;
        assign vld_o = !fifo_empty;
        assign data_o = fifo_mem[fifo_rd_ptr[$clog2(FIFO_DEPTH)-1:0]];
        always_ff @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                fifo_wr_ptr <= 0;
            end else begin
                if (vld_i && rdy_i) begin
                    fifo_wr_ptr <= fifo_wr_ptr + 1;
                end
            end
        end

        always_ff @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                fifo_rd_ptr <= 0;
            end else begin
                if (rdy_o && vld_o) begin
                    fifo_rd_ptr <= fifo_rd_ptr + 1;
                end
            end
        end

        for (genvar i = 0; i < FIFO_DEPTH; i++) begin
            if (RST_EN == 1) begin
                always_ff @(posedge clk or negedge rst_n) begin
                    if (!rst_n) begin
                        fifo_mem[i] <= 0;
                    end else if (vld_i && rdy_i && fifo_wr_ptr[$clog2(FIFO_DEPTH)-1:0] == i) begin
                        fifo_mem[i] <= data_i;
                    end
                end
            end else begin
                always_ff @(posedge clk) begin
                    if (vld_i && rdy_i && fifo_wr_ptr[$clog2(FIFO_DEPTH)-1:0] == i) begin
                        fifo_mem[i] <= data_i;
                    end
                end
            end
        end
    end

endmodule
